import StructType from 'ref-struct'
import ArrayType from 'ref-array'
import ref from 'ref'
import ffi from 'ffi'
import Jimp from 'jimp'
import fs from 'fs'

let Format = {
  ASVL_PAF_I420: 0x601,
  ASVL_PAF_NV12: 0x801,
  ASVL_PAF_NV21: 0x802,
  ASVL_PAF_YUYV: 0x501,
  ASVL_PAF_RGB24_B8G8R8: 0x201
}

let MInt32 = ref.types.int32
let MUInt32 = ref.types.uint32
let MLong = ref.types.long
let MIntPtr_t
if (process.arch === 'x64') {
  MIntPtr_t = ref.types.uint64
} else {
  MIntPtr_t = ref.types.uint32
}
let MPChar = ref.types.CString
let MHandleType = ref.refType(ref.types.void)
let MHandleRefType = ref.refType(MHandleType)
let MUInt8RefType = ref.refType(ref.types.uint8)

let ASVLOFFSCREEN = StructType({
  u32PixelArrayFormat: MUInt32,
  i32Width: MInt32,
  i32Height: MInt32,
  ppu8Plane: ArrayType(MHandleType, 4),
  pi32Pitch: ArrayType(MInt32, 4)
})

// 脸部信息
let MRECT = StructType({
  left: MInt32,
  top: MInt32,
  right: MInt32,
  bottom: MInt32
})

let libc_name = process.platform === 'win32' ? 'msvcrt' : 'libc'
let CLibrary = ffi.Library(libc_name, {
  'memcpy': [ref.refType(ref.types.void), [MIntPtr_t, MIntPtr_t, ref.types.size_t]],
  'malloc': [ref.refType(ref.types.void), [ref.types.size_t]],
  'free': [ref.types.void, [ref.refType(ref.types.void)]]
})

function loadYUVImage(filePath, width, height, format, result_callback) {
  fs.readFile(filePath, (err, yuvdata) => {
    if (err) throw err

    var asvl = new ASVLOFFSCREEN()
    asvl.u32PixelArrayFormat = format
    asvl.i32Width = width
    asvl.i32Height = height

    if (Format.ASVL_PAF_I420 === asvl.u32PixelArrayFormat) {
      asvl.pi32Pitch[0] = asvl.i32Width
      asvl.pi32Pitch[1] = asvl.i32Width / 2
      asvl.pi32Pitch[2] = asvl.i32Width / 2
      asvl.pi32Pitch[3] = 0

      asvl.gc_ppu8Plane0 = yuvdata.slice(0, asvl.pi32Pitch[0] * asvl.i32Height)
      asvl.gc_ppu8Plane1 = yuvdata.slice(asvl.pi32Pitch[0] * asvl.i32Height, asvl.pi32Pitch[1] * asvl.i32Height / 2)
      asvl.gc_ppu8Plane2 = yuvdata.slice(asvl.pi32Pitch[0] * asvl.i32Height + asvl.pi32Pitch[1] * asvl.i32Height / 2, asvl.pi32Pitch[2] * asvl.i32Height / 2)

      asvl.ppu8Plane[0] = asvl.gc_ppu8Plane0
      asvl.ppu8Plane[1] = asvl.gc_ppu8Plane1
      asvl.ppu8Plane[2] = asvl.gc_ppu8Plane2
      asvl.ppu8Plane[3] = ref.NULL
    } else if (Format.ASVL_PAF_NV12 === asvl.u32PixelArrayFormat) {
      asvl.pi32Pitch[0] = asvl.i32Width
      asvl.pi32Pitch[1] = asvl.i32Width
      asvl.pi32Pitch[2] = 0
      asvl.pi32Pitch[3] = 0

      asvl.gc_ppu8Plane0 = yuvdata.slice(0, asvl.pi32Pitch[0] * asvl.i32Height)
      asvl.gc_ppu8Plane1 = yuvdata.slice(asvl.pi32Pitch[0] * asvl.i32Height, asvl.pi32Pitch[1] * asvl.i32Height / 2)

      asvl.ppu8Plane[0] = asvl.gc_ppu8Plane0
      asvl.ppu8Plane[1] = asvl.gc_ppu8Plane1
      asvl.ppu8Plane[2] = ref.NULL
      asvl.ppu8Plane[3] = ref.NULL
    } else if (Format.ASVL_PAF_NV21 === asvl.u32PixelArrayFormat) {
      asvl.pi32Pitch[0] = asvl.i32Width
      asvl.pi32Pitch[1] = asvl.i32Width
      asvl.pi32Pitch[2] = 0
      asvl.pi32Pitch[3] = 0

      asvl.gc_ppu8Plane0 = yuvdata.slice(0, asvl.pi32Pitch[0] * asvl.i32Height)
      asvl.gc_ppu8Plane1 = yuvdata.slice(asvl.pi32Pitch[0] * asvl.i32Height, asvl.pi32Pitch[1] * asvl.i32Height / 2)

      asvl.ppu8Plane[0] = asvl.gc_ppu8Plane0
      asvl.ppu8Plane[1] = asvl.gc_ppu8Plane1
      asvl.ppu8Plane[2] = ref.NULL
      asvl.ppu8Plane[3] = ref.NULL
    } else if (Format.ASVL_PAF_YUYV === asvl.u32PixelArrayFormat) {
      asvl.pi32Pitch[0] = asvl.i32Width * 2
      asvl.pi32Pitch[1] = 0
      asvl.pi32Pitch[2] = 0
      asvl.pi32Pitch[3] = 0

      asvl.gc_ppu8Plane0 = yuvdata

      asvl.ppu8Plane[0] = asvl.gc_ppu8Plane0
      asvl.ppu8Plane[1] = ref.NULL
      asvl.ppu8Plane[2] = ref.NULL
      asvl.ppu8Plane[3] = ref.NULL
    } else if (Format.ASVL_PAF_RGB24_B8G8R8 === asvl.u32PixelArrayFormat) {
      asvl.pi32Pitch[0] = asvl.i32Width * 3
      asvl.pi32Pitch[1] = 0
      asvl.pi32Pitch[2] = 0
      asvl.pi32Pitch[3] = 0

      asvl.gc_ppu8Plane0 = yuvdata

      asvl.ppu8Plane[0] = asvl.gc_ppu8Plane0
      asvl.ppu8Plane[1] = ref.NULL
      asvl.ppu8Plane[2] = ref.NULL
      asvl.ppu8Plane[3] = ref.NULL
    } else {
      throw new Error('unsupported yuv format')
    }
    result_callback(0, asvl)
  })
}

function loadImage(filePath, result_callback) {
  Jimp.read(filePath, (err, img) => {
    if (err) throw err
    let y_channel = Buffer.alloc(img.bitmap.width * img.bitmap.height * 3)
    let asvl = new ASVLOFFSCREEN()
    img.scan(0, 0, img.bitmap.width, img.bitmap.height, function (i, j, idx) {
      let r = this.bitmap.data[idx + 0]
      let g = this.bitmap.data[idx + 1]
      let b = this.bitmap.data[idx + 2]

      y_channel[(j * this.bitmap.width + i) * 3] = b
      y_channel[(j * this.bitmap.width + i) * 3 + 1] = g
      y_channel[(j * this.bitmap.width + i) * 3 + 2] = r
    })
    asvl.u32PixelArrayFormat = Format.ASVL_PAF_RGB24_B8G8R8
    asvl.i32Width = img.bitmap.width
    asvl.i32Height = img.bitmap.height
    asvl.pi32Pitch[0] = asvl.i32Width * 3
    asvl.ppu8Plane[0] = y_channel
    result_callback(0, asvl)
  })
}

exports.MRECT = MRECT
exports.ASVLOFFSCREEN = ASVLOFFSCREEN
exports.Format = Format
exports.MInt32 = MInt32
exports.MLong = MLong
exports.MIntPtr_t = MIntPtr_t
exports.MPChar = MPChar
exports.MHandleType = MHandleType
exports.MHandleRefType = MHandleRefType
exports.MUInt8RefType = MUInt8RefType
exports.memcpy = CLibrary.memcpy
exports.malloc = CLibrary.malloc
exports.free = CLibrary.free
exports.loadImage = loadImage
exports.loadYUVImage = loadYUVImage
